package base.thread_JUC.demo01;

import java.util.concurrent.TimeUnit;

/**
 * volatile 只能保证内存可见性演示
 */
public class Client0 {

    // volatile 只能修饰成员变量
//    volatile static MyDemo myDemo = new MyDemo();
    public static void main(String[] args) {

        MyDemo myDemo = new MyDemo();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+" come in");
            try {
                // 保证让 main 线程进入 while 循环
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDemo.add();

            System.out.println(Thread.currentThread().getName()+" updated "+myDemo.num);
        },"t1").start();

        while (myDemo.num==0){
            // main线程一直在这里僵死，除非给MyDemo变量num前加 volatile 修饰
            /**
             * 下面sout语句不能要，否则看不到程序僵死状态：
             * 正解：while 存在于主线程中，线程都是操作自己工作内存中副本数据，t1线程执行完毕写回主存的数据没办法通知 main线程，
             * 导致 main线程一直僵死在while循环处。如果加了volatile，那么给 main线程加了内存屏障，每次while循环都读不到自己工作
             * 内存副本myDemo.num变量，但对主存myDemo.num可见，读取的是主存。如果此时t1执行结果写回了主存，那么main 线程就结束while循环。
             *
             * MYWHAT  https://lequ7.com/guan-yu-java-yi-ge-println-jing-ran-bi-volatile-hai-hao-shi.html
             * 如果要真是上面解释的这样的话，那么在while内部加上println输出语句为何就迫使main线程就从主存中读取myDemo.num了呢，
             * 而不是读取的还是自己工作内存中的myDemo.num=0？？
             * 答：结合字节码看（View->Show Bytecode），如果是空循环，cpu高速空转，没有时间分配时间片去更新主存（即main线程内存）
             * num数据到main线程的缓存（L1/L2/L3）中，
             * cpu永远只跟一级/二级/三级(L1/L2/L3)打交道，cpu在执行while空循环，从字节码可以看出
             * IFNE L4
             * GOTO L3两句汇编指令之间是没有间隙让CPU去更新主存中数到缓存中的，从而读取的缓存数据一直是num=0,如果while循环不是个空循环，有个耗时操作，比如
             * 这里的println打印操作，也会让cpu有时间去更新缓存数据，程序就不会僵死。
             * 而volatile可以强制CPU去更新缓存。
             */
//            System.out.println(Thread.currentThread().getName()+" -->"+myDemo.num);
        }

        System.out.println(Thread.currentThread().getName()+" end "+ myDemo.num);

    }
}

class MyDemo {
    int num =0;
//    volatile int num =0;
    public void add(){
        this.num = 10;
    }
}